﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using QLabValues;
using AtomicTypes;

namespace NumericalCalculations
{
    
    public partial class Functions
    {
        // F-ja max od dva kompleksna broja ih prvo poredi preko modula pa nam je zato potrebna ova pomocna f-ja
        double moduo(double x, double y)
        {
            return Math.Sqrt(x * x + y * y);
        }        

        public List<IQLabValue> max1(List<IQLabValue> o)
        {
            // Funkcija max moze da ima 1 ili 2 povratne vrednosti. U tom slucaju engine prosledjuje kao prvog elementa liste
            // broj ocekivanih argumenata na izlazu pa prvo proveravamo koliko ih ima i delimo na dva slucaja koja dalje obradjujemo
            #region Slucaj 2 povratne vrednosti
            if (o.ElementAt(0).Equals(2))
            {
                // Ovo je slucaj kada se vracaju dva argumenta
                // Ispitujemo neke degenerisane slucajeve
                if (o.Count() == 1)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> max \n Not enough input arguments."));
                }
                if (o.Count() > 4)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> max \n Too many input arguments."));
                }


                // Ukoliko je prosledjen 1 argument
                #region Jedan argument
                if (o.Count() == 2)
                {
                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        QFloat64NDimArray element = new QFloat64NDimArray(o.ElementAt(1));

                        List<IQLabValue> res = new List<IQLabValue>();

                        int[] size = element.DimensionSize;
                        // Krecemo diskusiju o broju dimenzija

                        // Ovo je slucaj kad radimo sa NDimArray-om
                        if (size.Length > 2)
                        {
                            int dim = 0;
                            // Uzimamo prvu non-singlton dimenziju NDimArray-a dim 
                            for (int i = 0; i < size.Length; i++)
                            {
                                if (size[i] > 1)
                                {
                                    dim = size[i];
                                    break;
                                }
                            }
                          
                            QFloat64NDimArray vals = new QFloat64NDimArray();
                            //IVector predstavlja pozicije argumeta koje vracamo u NDimArray vals
                            QFloat64NDimArray IVector = new QFloat64NDimArray();
                            Counter c = new Counter(element.DimensionSize);

                            // Uzimamo maksimalne vrednosti po dimenziji dim i smestamo ih u vals tako da vals ima dimenizje iste kao pocetni argument s tim da
                            // na poziciji dim ima singlton (1)

                            for (c.reset(); !c.isAtEnd(); c.plusPlus())
                            {
                                int[] index = c.getCurrent();
                                int[] eltocmp = c.getCurrent();
                                for (int i = 0; i < dim; i++)
                                {
                                    if (moduo((double)vals[index].Real, (double)vals[index].Imag) < moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        vals[index] = element[eltocmp];
                                        IVector[index] = new AtomicTypes.QFloat64(i); 
                                    }
                                    else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        if (vals[index].Real > element[index].Real)
                                        {
                                            vals[index] = element[eltocmp];
                                            IVector[index] = new AtomicTypes.QFloat64(i);
                                        }
                                        else if (vals[index].Real == element[index].Real)
                                        {
                                            if (vals[index].Imag < element[index].Imag)
                                            {
                                                vals[index] = element[eltocmp];
                                                IVector[index] = new AtomicTypes.QFloat64(i);
                                            }
                                        }
                                    }
                                    eltocmp[dim]++;
                                }
                            }
                            res.Add(vals);
                            res.Add(IVector);
                            return res;

                        }
                        // Proveravamo dimenzije <= 2
                        else
                        {
                            // Proverim da li je vektor - dimenzije 1,n
                            if (size[0] == 1)
                            {
                                QFloat64 max = element[0, 0];
                                //IVector predstavlja pozicije argumeta koje vracamo u NDimArray vals
                                QFloat64 IVector = new AtomicTypes.QFloat64(1);
                                for (int i = 1; i < size[1]; i++)
                                {
                                    QFloat64 curr = element[0, i];
                                    if (curr.Real > max.Real)
                                    {
                                        max = new QFloat64(curr.Real, curr.Imag);
                                        IVector = new AtomicTypes.QFloat64(i+1);
                                    }
                                    if (curr.Real == max.Real)
                                        if (curr.Imag > max.Imag)
                                        {
                                            max = new QFloat64(curr.Real, curr.Imag);
                                            IVector = new AtomicTypes.QFloat64(i+1);
                                        }

                                }

                                List<IQLabValue> res1 = new List<IQLabValue>();
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { max }, new int[] { 1, 1 }));
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { IVector }, new int[] { 1, 1 }));
                                return res1;
                            }
                            // Dvodimenzionalna matrica
                            else
                            {
                                QFloat64NDimArray res1 = new QFloat64NDimArray(new int[] { 1, size[1] });
                                QFloat64NDimArray IVector = new QFloat64NDimArray(new int[] { 1, size[1] });
                                for (int j = 0; j < size[1]; j++)
                                {
                                    res1[0, j] = element[0, j];
                                    IVector[0, j] = new AtomicTypes.QFloat64(1);

                                    for (int i = 1; i < size[0]; i++)
                                    {
                                        QFloat64 curr = element[i, j];
                                        if (curr.Real > res1[0, j].Real)
                                        {
                                            res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                            IVector[0, j] = new AtomicTypes.QFloat64(i+1);
                                        }
                                        if (curr.Real == res1[0, j].Real)
                                            if (curr.Imag > res1[0, j].Imag)
                                            {
                                                res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                                IVector[0, j] = new AtomicTypes.QFloat64(i+1);
                                            }
                                    }
                                }

                                List<IQLabValue> res1List = new List<IQLabValue>();
                                res1List.Add(res1);
                                res1List.Add(IVector);
                                return res1List;
                            }
                        }


                    }
                    else
                    // nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'max' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion

                // Ukoliko su prosledjena 2 argumenta
                #region Dva argumenta
                else if (o.Count() == 3)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> max \n MAX with two matrices to compare and two output arguments is not supported."));
                }
                #endregion
                // Ukoliko su prosledjena 3 argumenta
                #region Tri argumenta
                if (o.Count() == 4)
                {
                    List<IQLabValue> res = new List<IQLabValue>();

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo pojedinacno prosledjene elemente
                        QFloat64NDimArray element1 = new QFloat64NDimArray(o.ElementAt(1));
                        QCharNDimArray element2 = new QCharNDimArray(o.ElementAt(2));
                        QInt64NDimArray element3 = new QInt64NDimArray(o.ElementAt(3));
                        QFloat64NDimArray IVector = new QFloat64NDimArray();

                        int[] size2 = element2.DimensionSize;
                        int[] size1 = element1.DimensionSize;
                        int[] size3 = element3.DimensionSize;

                        // Proveravamo da li su drugi i treci argument odgovarajuceg formata 
                        if ((size2.Length != 2) || (!(element2[0].ToString() == "[") && !(element2[1].ToString() == "]")))
                        {
                            throw (new NumericalCalculationsException("Incorrect format of second argument"));
                        }
                        if (size3.Length != 1)
                        {
                            throw (new NumericalCalculationsException("Incorrect format of third argument"));
                        }
                        // Ukoliko je vrednost treceg elementa veca nego broj dimenzija NDImArray-a vraca se ceo prvi argument(tako radi Matlab)
                        if (Convert.ToInt32(element3[0]) > size1.Length)
                        {
                            IVector.Equals(1);
                            res.Add(element1);
                            res.Add(IVector);
                            return res;
                        }
                        // Ukoliko je vrednost dimenzije negativna ili 0 vraca exception
                        if (Convert.ToInt32(element3[0]) <= 0)
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> max \n Dimension argument must be a positive integer scalar within indexing range."));
                        }

                        // Treba naknadno dodati jos exception-a u slucaju pogresnog unosa drugog elementa

                        Counter c = new Counter(element1.DimensionSize);
                        QFloat64NDimArray vals = new QFloat64NDimArray();
                        //QFloat64NDimArray I = new QFloat64NDimArray();

                        // Fiksiramo sve sem dimenzije cija je vrednost data kao treci prosledjeni argument
                        for (c.reset(); !c.isAtEnd(); c.plusPlus())
                        {

                            int[] index = c.getCurrent();
                            int[] eltocmp = c.getCurrent();
                            for (int i = 0; i < Convert.ToInt32(element3[0]); i++)
                            {
                                if (moduo((double)vals[index].Real, (double)vals[index].Imag) < moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    vals[index] = element1[eltocmp];
                                    IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                }
                                else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    if (vals[index].Real > element1[index].Real)
                                    {
                                        vals[index] = element1[eltocmp];
                                        IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                    }
                                    else if (vals[index].Real == element1[index].Real)
                                    {
                                        if (vals[index].Imag < element1[index].Imag)
                                        {
                                            vals[index] = element1[eltocmp];
                                            IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                        }
                                    }
                                }
                                eltocmp[Convert.ToInt32(element3[0])]++;
                            }
                        }
                        res.Add(vals);
                        res.Add(IVector);
                        return res;
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'max' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion
            }
#endregion
            else
            {
                #region Slucaj 1 povratne vrednosti
                // Ovo je slucaj kada se vraca jedan argument
                // Ispitujemo neke degenerisane slucajeve
                if (o.Count() == 1)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> max \n Not enough input arguments."));
                }
                if (o.Count() > 4)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> max \n Too many input arguments."));
                }


                // Ukoliko je prosledjen 1 argument
                #region Jedan argument
                if (o.Count() == 2)
                {
                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        QFloat64NDimArray element = new QFloat64NDimArray(o.ElementAt(1));

                        List<IQLabValue> res = new List<IQLabValue>();

                        int[] size = element.DimensionSize;
                        // Krecemo diskusiju o broju dimenzija

                        // Ovo je slucaj kad radimo sa NDimArray-om
                        if (size.Length > 2)
                        {
                            int dim = 0;
                            // Uzimamo prvu non-singlton dimenziju NDimArray-a dim 
                            for (int i = 0; i < size.Length; i++)
                            {
                                if (size[i] > 1)
                                {
                                    dim = size[i];
                                    break;
                                }
                            }
                            QFloat64NDimArray vals = new QFloat64NDimArray();
                            Counter c = new Counter(element.DimensionSize);

                            // Uzimamo maksimalne vrednosti po dimenziji dim i smestamo ih u vals tako da vals ima dimenizje iste kao pocetni argument s tim da
                            // na poziciji dim ima singlton (1)

                            for (c.reset(); !c.isAtEnd(); c.plusPlus())
                            {
                                int[] index = c.getCurrent();
                                int[] eltocmp = c.getCurrent();
                                for (int i = 0; i < dim; i++)
                                {
                                    if (moduo((double)vals[index].Real, (double)vals[index].Imag) < moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                        vals[index] = element[eltocmp];
                                    else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        if (vals[index].Real > element[index].Real)
                                            vals[index] = element[eltocmp];
                                        else if (vals[index].Real == element[index].Real)
                                        {
                                            if (vals[index].Imag < element[index].Imag)
                                                vals[index] = element[eltocmp];
                                        }
                                    }
                                    eltocmp[dim]++;
                                }
                            }
                            res.Add(vals);
                            return res;

                        }
                        // Proveravamo dimenzije <= 2
                        else
                        {
                            // Proverim da li je vektor - dimenzije 1,n
                            if (size[0] == 1)
                            {
                                QFloat64 max = element[0, 0];
                                for (int i = 1; i < size[1]; i++)
                                {
                                    QFloat64 curr = element[0, i];
                                    if (curr.Real > max.Real)
                                        max = new QFloat64(curr.Real, curr.Imag);
                                    if (curr.Real == max.Real)
                                        if (curr.Imag > max.Imag)
                                            max = new QFloat64(curr.Real, curr.Imag);

                                }

                                List<IQLabValue> res1 = new List<IQLabValue>();
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { max }, new int[] { 1, 1 }));
                                return res1;
                            }
                            // Dvodimenzionalna matrica
                            else
                            {
                                QFloat64NDimArray res1 = new QFloat64NDimArray(new int[] { 1, size[1] });
                                for (int j = 0; j < size[1]; j++)
                                {
                                    res1[0, j] = element[0, j];
                                    for (int i = 1; i < size[0]; i++)
                                    {
                                        QFloat64 curr = element[i, j];
                                        if (curr.Real > res1[0, j].Real)
                                            res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                        if (curr.Real == res1[0, j].Real)
                                            if (curr.Imag > res1[0, j].Imag)
                                                res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                    }
                                }

                                List<IQLabValue> res1List = new List<IQLabValue>();
                                res1List.Add(res1);
                                return res1List;
                            }
                        }


                    }
                    else
                    // nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'max' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion

                // Ukoliko su prosledjena 2 argumenta
                #region Dva argumenta
                else if (o.Count() == 3)
                {

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        List<QFloat64NDimArray> of = new List<QFloat64NDimArray>();
                        foreach (var oel in o)
                            of.Add(new QFloat64NDimArray(oel));

                        // Proveravamo da li su istih dimenzija
                        if (Helper.checkDimensions(of.ElementAt(1), of.ElementAt(2)) == false)
                            throw (new NumericalCalculationsException("Error using ==> plus \n Matrix dimensions must agree."));
                        else
                        {
                            // Poredimo module, potom realne delove, potom imaginarne
                            // Ako su moduli isti vraca onaj sa manjom realnom vrednoscu (tako radi matlab)
                            // Ako su i moduli i realni delovi isti uzima onaj sa vecim imaginarnim delom (tako radi matlab)
                            
                            List<IQLabValue> res = new List<IQLabValue>();
                            AtomicTypes.QFloat64[] vals = new AtomicTypes.QFloat64[of.ElementAt(1).Data.Count()];
                            for (int i = 0; i < vals.Count(); i++)
                            {
                                double x1 = of.ElementAt(1).Data[i].Real;
                                double x2 = of.ElementAt(2).Data[i].Real;
                                double y1 = of.ElementAt(1).Data[i].Imag;
                                double y2 = of.ElementAt(2).Data[i].Imag;
                                if (moduo(x1, y1) > moduo(x2, y2))
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else if (moduo(x1, y1) < moduo(x2, y2))
                                    vals[i] = new AtomicTypes.QFloat64(x2, y2);
                                else if (x1 > x2)
                                    vals[i] = new AtomicTypes.QFloat64(x2, y2);
                                else if (x1 < x2)
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else if (y1 > y2)
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else vals[i] = new AtomicTypes.QFloat64(x2, y2);
                            }
                            QFloat64NDimArray qf = new QFloat64NDimArray(vals, of.ElementAt(1).DimensionSize);
                            res.Add(qf);
                            return res;
                        }
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'max' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion
                // Ukoliko su prosledjena 3 argumenta
                #region Tri argumenta
                if (o.Count() == 4)
                {
                    List<IQLabValue> res = new List<IQLabValue>();

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo pojedinacno prosledjene elemente
                        QFloat64NDimArray element1 = new QFloat64NDimArray(o.ElementAt(1));
                        QCharNDimArray element2 = new QCharNDimArray(o.ElementAt(2));
                        QInt64NDimArray element3 = new QInt64NDimArray(o.ElementAt(3));

                        int[] size2 = element2.DimensionSize;
                        int[] size1 = element1.DimensionSize;
                        int[] size3 = element3.DimensionSize;

                        // Proveravamo da li su drugi i treci argument odgovarajuceg formata 
                        if ((size2.Length != 2) || (!(element2[0].ToString() == "[") && !(element2[1].ToString() == "]")))
                        {
                            throw (new NumericalCalculationsException("Incorrect format of second argument"));
                        }
                        if (size3.Length != 1)
                        {
                            throw (new NumericalCalculationsException("Incorrect format of third argument"));
                        }
                        // Ukoliko je vrednost treceg elementa veca nego broj dimenzija NDImArray-a vraca se ceo prvi argument(tako radi Matlab)
                        if (Convert.ToInt32(element3[0]) > size1.Length)
                        {
                            res.Add(element1);
                            return res;
                        }
                        // Ukoliko je vrednost dimenzije negativna ili 0 vraca exception
                        if (Convert.ToInt32(element3[0]) <= 0)
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> max \n Dimension argument must be a positive integer scalar within indexing range."));
                        }

                        // Treba naknadno dodati jos exception-a u slucaju pogresnog unosa drugog elementa

                        Counter c = new Counter(element1.DimensionSize);
                        QFloat64NDimArray vals = new QFloat64NDimArray();

                        // Fiksiramo sve sem dimenzije cija je vrednost data kao treci prosledjeni argument
                        for (c.reset(); !c.isAtEnd(); c.plusPlus())
                        {

                            int[] index = c.getCurrent();
                            int[] eltocmp = c.getCurrent();
                            for (int i = 0; i < Convert.ToInt32(element3[0]); i++)
                            {
                                if (moduo((double)vals[index].Real, (double)vals[index].Imag) < moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                    vals[index] = element1[eltocmp];
                                else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    if (vals[index].Real > element1[index].Real)
                                        vals[index] = element1[eltocmp];
                                    else if (vals[index].Real == element1[index].Real)
                                    {
                                        if (vals[index].Imag < element1[index].Imag)
                                            vals[index] = element1[eltocmp];
                                    }
                                }
                                eltocmp[Convert.ToInt32(element3[0])]++;
                            }
                        }
                        res.Add(vals);
                        return res;
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'max' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion                       

                #endregion
            }
            
            return null;
        }


        public List<IQLabValue> min1(List<IQLabValue> o)
        {
            // Funkcija min moze da ima 1 ili 2 povratne vrednosti. U tom slucaju engine prosledjuje kao prvog elementa liste
            // broj ocekivanih argumenata na izlazu pa prvo proveravamo koliko ih ima i delimo na dva slucaja koja dalje obradjujemo
            #region Slucaj 2 povratne vrednosti
            if (o.ElementAt(0).Equals(2))
            {
                // Ovo je slucaj kada se vracaju dva argumenta
                // Ispitujemo neke degenerisane slucajeve
                if (o.Count() == 1)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> min \n Not enough input arguments."));
                }
                if (o.Count() > 4)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> min \n Too many input arguments."));
                }


                // Ukoliko je prosledjen 1 argument
                #region Jedan argument
                if (o.Count() == 2)
                {
                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        QFloat64NDimArray element = new QFloat64NDimArray(o.ElementAt(1));

                        List<IQLabValue> res = new List<IQLabValue>();

                        int[] size = element.DimensionSize;
                        // Krecemo diskusiju o broju dimenzija

                        // Ovo je slucaj kad radimo sa NDimArray-om
                        if (size.Length > 2)
                        {
                            int dim = 0;
                            // Uzimamo prvu non-singlton dimenziju NDimArray-a dim 
                            for (int i = 0; i < size.Length; i++)
                            {
                                if (size[i] > 1)
                                {
                                    dim = size[i];
                                    break;
                                }
                            }
                            QFloat64NDimArray vals = new QFloat64NDimArray();
                            //IVector predstavlja pozicije argumeta koje vracamo u NDimArray vals
                            QFloat64NDimArray IVector = new QFloat64NDimArray();
                            Counter c = new Counter(element.DimensionSize);

                            // Uzimamo minimalne vrednosti po dimenziji dim i smestamo ih u vals tako da vals ima dimenizje iste kao pocetni argument s tim da
                            // na poziciji dim ima singlton (1)

                            for (c.reset(); !c.isAtEnd(); c.plusPlus())
                            {
                                int[] index = c.getCurrent();
                                int[] eltocmp = c.getCurrent();
                                for (int i = 0; i < dim; i++)
                                {
                                    if (moduo((double)vals[index].Real, (double)vals[index].Imag) > moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        vals[index] = element[eltocmp];
                                        IVector[index] = new AtomicTypes.QFloat64(i);
                                    }
                                    else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        if (vals[index].Real < element[index].Real)
                                        {
                                            vals[index] = element[eltocmp];
                                            IVector[index] = new AtomicTypes.QFloat64(i);
                                        }
                                        else if (vals[index].Real == element[index].Real)
                                        {
                                            if (vals[index].Imag > element[index].Imag)
                                            {
                                                vals[index] = element[eltocmp];
                                                IVector[index] = new AtomicTypes.QFloat64(i);
                                            }
                                        }
                                    }
                                    eltocmp[dim]++;
                                }
                            }
                            res.Add(vals);
                            res.Add(IVector);
                            return res;

                        }
                        // Proveravamo dimenzije <= 2
                        else
                        {
                            // Proverim da li je vektor - dimenzije 1,n
                            if (size[0] == 1)
                            {
                                QFloat64 min = element[0, 0];
                                QFloat64 IVector = new AtomicTypes.QFloat64(1);
                                for (int i = 1; i < size[1]; i++)
                                {
                                    QFloat64 curr = element[0, i];
                                    if (curr.Real < min.Real)
                                    {
                                        min = new QFloat64(curr.Real, curr.Imag);
                                        IVector = new AtomicTypes.QFloat64(i + 1);
                                    }
                                    if (curr.Real == min.Real)
                                        if (curr.Imag > min.Imag)
                                        {
                                            min = new QFloat64(curr.Real, curr.Imag);
                                            IVector = new AtomicTypes.QFloat64(i + 1);
                                        }

                                }

                                List<IQLabValue> res1 = new List<IQLabValue>();
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { min }, new int[] { 1, 1 }));
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { IVector }, new int[] { 1, 1 }));
                                return res1;
                            }
                            // Dvodimenzionalna matrica
                            else
                            {
                                QFloat64NDimArray res1 = new QFloat64NDimArray(new int[] { 1, size[1] });
                                QFloat64NDimArray IVector = new QFloat64NDimArray(new int[] { 1, size[1] });
                                for (int j = 0; j < size[1]; j++)
                                {
                                    res1[0, j] = element[0, j];
                                    IVector[0, j] = new AtomicTypes.QFloat64(1);

                                    for (int i = 1; i < size[0]; i++)
                                    {
                                        QFloat64 curr = element[i, j];
                                        if (curr.Real < res1[0, j].Real)
                                        {
                                            res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                            IVector[0, j] = new AtomicTypes.QFloat64(i + 1);
                                        }
                                        if (curr.Real == res1[0, j].Real)
                                            if (curr.Imag < res1[0, j].Imag)
                                            {
                                                res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                                IVector[0, j] = new AtomicTypes.QFloat64(i + 1);
                                            }
                                    }
                                }

                                List<IQLabValue> res1List = new List<IQLabValue>();
                                res1List.Add(res1);
                                res1List.Add(IVector);
                                return res1List;
                            }
                        }


                    }
                    else
                    // nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'min' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion

                // Ukoliko su prosledjena 2 argumenta
                #region Dva argumenta
                else if (o.Count() == 3)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> min \n MIN with two matrices to compare and two output arguments is not supported."));
                }
                #endregion
                // Ukoliko su prosledjena 3 argumenta
                #region Tri argumenta
                if (o.Count() == 4)
                {
                    List<IQLabValue> res = new List<IQLabValue>();

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo pojedinacno prosledjene elemente
                        QFloat64NDimArray element1 = new QFloat64NDimArray(o.ElementAt(1));
                        QCharNDimArray element2 = new QCharNDimArray(o.ElementAt(2));
                        QInt64NDimArray element3 = new QInt64NDimArray(o.ElementAt(3));
                        QFloat64NDimArray IVector = new QFloat64NDimArray();

                        int[] size2 = element2.DimensionSize;
                        int[] size1 = element1.DimensionSize;
                        int[] size3 = element3.DimensionSize;

                        // Proveravamo da li su drugi i treci argument odgovarajuceg formata 
                        if ((size2.Length != 2) || (!(element2[0].ToString() == "[") && !(element2[1].ToString() == "]")))
                        {
                            throw (new NumericalCalculationsException("Incorrect format of second argument"));
                        }
                        if (size3.Length != 1)
                        {
                            throw (new NumericalCalculationsException("Incorrect format of third argument"));
                        }
                        // Ukoliko je vrednost treceg elementa veca nego broj dimenzija NDImArray-a vraca se ceo prvi argument(tako radi Matlab)
                        if (Convert.ToInt32(element3[0]) > size1.Length)
                        {
                            IVector.Equals(1);
                            res.Add(element1);
                            res.Add(IVector);
                            return res;
                        }
                        // Ukoliko je vrednost dimenzije negativna ili 0 vraca exception
                        if (Convert.ToInt32(element3[0]) <= 0)
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> min \n Dimension argument must be a positive integer scalar within indexing range."));
                        }

                        // Treba naknadno dodati jos exception-a u slucaju pogresnog unosa drugog elementa
                        // Problem je sledeci: Ukoliko je ulaz nepravilan, sintaksno, MatLab vraca gresku na osnovu tekstualne analize i 
                        // i vraca tacan problem greske (npr.: neodgovarajuca promensljiva, nepostojeca promenljiva itd.) a mi ne znamo tacno
                        // o kojoj gresci se radi na osnou dobijenih argumenata. Predlog: Opsti exception za nepravilan unos + uslovi koji
                        // se moraju ispuniti da bi f-ja mogla da se izvrsi


                        Counter c = new Counter(element1.DimensionSize);
                        QFloat64NDimArray vals = new QFloat64NDimArray();
                        //QFloat64NDimArray I = new QFloat64NDimArray();

                        // Fiksiramo sve sem dimenzije cija je vrednost data kao treci prosledjeni argument
                        for (c.reset(); !c.isAtEnd(); c.plusPlus())
                        {

                            int[] index = c.getCurrent();
                            int[] eltocmp = c.getCurrent();
                            for (int i = 0; i < Convert.ToInt32(element3[0]); i++)
                            {
                                if (moduo((double)vals[index].Real, (double)vals[index].Imag) > moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    vals[index] = element1[eltocmp];
                                    IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                }
                                else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    if (vals[index].Real < element1[index].Real)
                                    {
                                        vals[index] = element1[eltocmp];
                                        IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                    }
                                    else if (vals[index].Real == element1[index].Real)
                                    {
                                        if (vals[index].Imag > element1[index].Imag)
                                        {
                                            vals[index] = element1[eltocmp];
                                            IVector[index] = new AtomicTypes.QFloat64(i + 1);
                                        }
                                    }
                                }
                                eltocmp[Convert.ToInt32(element3[0])]++;
                            }
                        }
                        res.Add(vals);
                        res.Add(IVector);
                        return res;
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'min' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion
            }
            #endregion
            else
            {
                #region Slucaj 1 povratne vrednosti
                // Ovo je slucaj kada se vraca jedan argument
                // Ispitujemo neke degenerisane slucajeve
                if (o.Count() == 1)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> min \n Not enough input arguments."));
                }
                if (o.Count() > 4)
                {
                    throw (new NumericalCalculationsException("??? Error using ==> min \n Too many input arguments."));
                }


                // Ukoliko je prosledjen 1 argument
                #region Jedan argument
                if (o.Count() == 2)
                {
                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        QFloat64NDimArray element = new QFloat64NDimArray(o.ElementAt(1));

                        List<IQLabValue> res = new List<IQLabValue>();

                        int[] size = element.DimensionSize;
                        // Krecemo diskusiju o broju dimenzija

                        // Ovo je slucaj kad radimo sa NDimArray-om
                        if (size.Length > 2)
                        {
                            int dim = 0;
                            // Uzimamo prvu non-singlton dimenziju NDimArray-a dim 
                            for (int i = 0; i < size.Length; i++)
                            {
                                if (size[i] > 1)
                                {
                                    dim = size[i];
                                    break;
                                }
                            }
                            QFloat64NDimArray vals = new QFloat64NDimArray();
                            Counter c = new Counter(element.DimensionSize);

                            // Uzimamo minimalne vrednosti po dimenziji dim i smestamo ih u vals tako da vals ima dimenizje iste kao pocetni argument s tim da
                            // na poziciji dim ima singlton (1)

                            for (c.reset(); !c.isAtEnd(); c.plusPlus())
                            {
                                int[] index = c.getCurrent();
                                int[] eltocmp = c.getCurrent();
                                for (int i = 0; i < dim; i++)
                                {
                                    if (moduo((double)vals[index].Real, (double)vals[index].Imag) > moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                        vals[index] = element[eltocmp];
                                    else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element[eltocmp].Real, (double)element[eltocmp].Imag))
                                    {
                                        if (vals[index].Real < element[index].Real)
                                            vals[index] = element[eltocmp];
                                        else if (vals[index].Real == element[index].Real)
                                        {
                                            if (vals[index].Imag > element[index].Imag)
                                                vals[index] = element[eltocmp];
                                        }
                                    }
                                    eltocmp[dim]++;
                                }
                            }
                            res.Add(vals);
                            return res;

                        }
                        // Proveravamo dimenzije <= 2
                        else
                        {
                            // Proverim da li je vektor - dimenzije 1,n
                            if (size[0] == 1)
                            {
                                QFloat64 max = element[0, 0];
                                for (int i = 1; i < size[1]; i++)
                                {
                                    QFloat64 curr = element[0, i];
                                    if (curr.Real < max.Real)
                                        max = new QFloat64(curr.Real, curr.Imag);
                                    if (curr.Real == max.Real)
                                        if (curr.Imag < max.Imag)
                                            max = new QFloat64(curr.Real, curr.Imag);

                                }

                                List<IQLabValue> res1 = new List<IQLabValue>();
                                res1.Add(new QFloat64NDimArray(new QFloat64[] { max }, new int[] { 1, 1 }));
                                return res1;
                            }
                            // Dvodimenzionalna matrica
                            else
                            {
                                QFloat64NDimArray res1 = new QFloat64NDimArray(new int[] { 1, size[1] });
                                for (int j = 0; j < size[1]; j++)
                                {
                                    res1[0, j] = element[0, j];
                                    for (int i = 1; i < size[0]; i++)
                                    {
                                        QFloat64 curr = element[i, j];
                                        if (curr.Real < res1[0, j].Real)
                                            res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                        if (curr.Real == res1[0, j].Real)
                                            if (curr.Imag < res1[0, j].Imag)
                                                res1[0, j] = new QFloat64(curr.Real, curr.Imag);
                                    }
                                }

                                List<IQLabValue> res1List = new List<IQLabValue>();
                                res1List.Add(res1);
                                return res1List;
                            }
                        }


                    }
                    else
                    // nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'min' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion

                // Ukoliko su prosledjena 2 argumenta
                #region Dva argumenta
                else if (o.Count() == 3)
                {

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo u float64
                        List<QFloat64NDimArray> of = new List<QFloat64NDimArray>();
                        foreach (var oel in o)
                            of.Add(new QFloat64NDimArray(oel));

                        // Proveravamo da li su istih dimenzija
                        if (Helper.checkDimensions(of.ElementAt(1), of.ElementAt(2)) == false)
                            throw (new NumericalCalculationsException("Error using ==> plus \n Matrix dimensions must agree."));
                        else
                        {
                            // Poredimo module, potom realne delove, potom imaginarne
                            // Ako su moduli isti vraca onaj sa vecom realnom vrednoscu (tako radi matlab)
                            // Ako su i moduli i realni delovi isti uzima onaj sa manjim imaginarnim delom (tako radi matlab)
                            List<IQLabValue> res = new List<IQLabValue>();
                            AtomicTypes.QFloat64[] vals = new AtomicTypes.QFloat64[of.ElementAt(1).Data.Count()];
                            for (int i = 0; i < vals.Count(); i++)
                            {
                                double x1 = of.ElementAt(1).Data[i].Real;
                                double x2 = of.ElementAt(2).Data[i].Real;
                                double y1 = of.ElementAt(1).Data[i].Imag;
                                double y2 = of.ElementAt(2).Data[i].Imag;
                                if (moduo(x1, y1) < moduo(x2, y2))
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else if (moduo(x1, y1) > moduo(x2, y2))
                                    vals[i] = new AtomicTypes.QFloat64(x2, y2);
                                else if (x1 < x2)
                                    vals[i] = new AtomicTypes.QFloat64(x2, y2);
                                else if (x1 > x2)
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else if (y1 < y2)
                                    vals[i] = new AtomicTypes.QFloat64(x1, y1);
                                else vals[i] = new AtomicTypes.QFloat64(x2, y2);
                            }
                            QFloat64NDimArray qf = new QFloat64NDimArray(vals, of.ElementAt(1).DimensionSize);
                            res.Add(qf);
                            return res;
                        }
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'min' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion
                // Ukoliko su prosledjena 3 argumenta
                #region Tri argumenta
                if (o.Count() == 4)
                {
                    List<IQLabValue> res = new List<IQLabValue>();

                    if (Helper.canConvertListToEnum(o, IQLabValueType.QFloat64NDimArray))
                    {
                        // Konvertujemo pojedinacno prosledjene elemente
                        QFloat64NDimArray element1 = new QFloat64NDimArray(o.ElementAt(1));
                        QCharNDimArray element2 = new QCharNDimArray(o.ElementAt(2));
                        QInt64NDimArray element3 = new QInt64NDimArray(o.ElementAt(3));

                        int[] size2 = element2.DimensionSize;
                        int[] size1 = element1.DimensionSize;
                        int[] size3 = element3.DimensionSize;

                        // Proveravamo da li su drugi i treci argument odgovarajuceg formata 
                        if ((size2.Length != 2) || (!(element2[0].ToString() == "[") && !(element2[1].ToString() == "]")))
                        {
                            throw (new NumericalCalculationsException("Incorrect format of second argument"));
                        }
                        if (size3.Length != 1)
                        {
                            throw (new NumericalCalculationsException("Incorrect format of third argument"));
                        }
                        // Ukoliko je vrednost treceg elementa veca nego broj dimenzija NDImArray-a vraca se ceo prvi argument(tako radi Matlab)
                        if (Convert.ToInt32(element3[0]) > size1.Length)
                        {
                            res.Add(element1);
                            return res;
                        }
                        // Ukoliko je vrednost dimenzije negativna ili 0 vraca exception
                        if (Convert.ToInt32(element3[0]) <= 0)
                        {
                            throw (new NumericalCalculationsException("??? Error using ==> min \n Dimension argument must be a positive integer scalar within indexing range."));
                        }

                        // Treba naknadno dodati jos exception-a u slucaju pogresnog unosa drugog elementa
                        // Problem je sledeci: Ukoliko je ulaz nepravilan, sintaksno, MatLab vraca gresku na osnovu tekstualne analize i 
                        // i vraca tacan problem greske (npr.: neodgovarajuca promenljiva, nepostojeca promenljiva itd.) a mi ne znamo tacno
                        // o kojoj gresci se radi na osnovu dobijenih argumenata. Predlog: Opsti exception za nepravilan unos + uslovi koji
                        // se moraju ispuniti da bi f-ja mogla da se izvrsi

                        Counter c = new Counter(element1.DimensionSize);
                        QFloat64NDimArray vals = new QFloat64NDimArray();

                        // Fiksiramo sve sem dimenzije cija je vrednost data kao treci prosledjeni argument
                        for (c.reset(); !c.isAtEnd(); c.plusPlus())
                        {

                            int[] index = c.getCurrent();
                            int[] eltocmp = c.getCurrent();
                            for (int i = 0; i < Convert.ToInt32(element3[0]); i++)
                            {
                                if (moduo((double)vals[index].Real, (double)vals[index].Imag) > moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                    vals[index] = element1[eltocmp];
                                else if (moduo((double)vals[index].Real, (double)vals[index].Imag) == moduo((double)element1[eltocmp].Real, (double)element1[eltocmp].Imag))
                                {
                                    if (vals[index].Real < element1[index].Real)
                                        vals[index] = element1[eltocmp];
                                    else if (vals[index].Real == element1[index].Real)
                                    {
                                        if (vals[index].Imag > element1[index].Imag)
                                            vals[index] = element1[eltocmp];
                                    }
                                }
                                eltocmp[Convert.ToInt32(element3[0])]++;
                            }
                        }
                        res.Add(vals);
                        return res;
                    }
                    else
                    // Nije uspelo konvertovanje, treba da ispise da ne moze da uzme te argumente
                    {
                        throw (new NumericalCalculationsException("??? Undefined function or method 'min' for input arguments of type " +
                            Helper.PrintEnumName(Helper.TypeOfThatCouldNotConvert(o, IQLabValueType.QFloat64NDimArray)) + "."));
                    }
                }
                #endregion

                #endregion
            }

            return null;
        }
    } 
}
